package com.quanyan.operative.service.impl;

import com.alibaba.fastjson.JSON;
import com.lefu.commons.utils.lang.StringUtils;
import com.quanyan.api.APIRequest;
import com.quanyan.api.APIResponse;
import com.quanyan.api.enums.AssetsType;
import com.quanyan.assets.entity.TbUserAssets;
import com.quanyan.assets.request.ReqUserAccumulateModify;
import com.quanyan.assets.request.ReqUserInfo;
import com.quanyan.assets.response.RespUserAccumulateModify;
import com.quanyan.assets.service.UserAssetsService;
import com.quanyan.common.Plugin.Page;
import com.quanyan.common.constants.Constants;
import com.quanyan.common.exception.BizException;
import com.quanyan.common.http.HttpClientUtils;
import com.quanyan.messagepush.entity.Alert;
import com.quanyan.messagepush.entity.Payload;
import com.quanyan.messagepush.entity.PushMessage;
import com.quanyan.messagepush.entity.ReqPushMessage;
import com.quanyan.messagepush.enums.BoxName;
import com.quanyan.messagepush.enums.BoxType;
import com.quanyan.messagepush.enums.MsgType;
import com.quanyan.operative.db.TbOpActivity;
import com.quanyan.operative.mapper.*;
import com.quanyan.operative.model.db.*;
import com.quanyan.operative.model.resp.*;
import com.quanyan.operative.service.SnatchService;
import com.quanyan.sms.service.SmsService;
import com.quanyan.user.response.RespUserInfoBase;
import com.quanyan.user.serviceFacade.UserServiceFacade;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import sms.SmsApi;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * Created by huadongyang on 2016/12/30.
 */
@Service
public class SnatchServiceImpl implements SnatchService {
    private static final Logger logger = LoggerFactory.getLogger(SnatchServiceImpl.class);

    private static String LARK_LOGO = "lark_icon.png";

    private static String DOWNLOAD_LINK = "http://t.cn/RMLQl64";

    private static final String SYSTEM_MESSAGE_URL = "http://quncao.b0.upaiyun.com/system/message/default/lark_f0bd80fb35f46bed00a15f1aebcb043b.png";
    private static final int PADDING_DIGIT = 5;
    private static final int LOTTERY_TIME_DELAY = 3;

    private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(2 * Runtime.getRuntime().availableProcessors());

    @Value("${message.push.service.url}")
    private String messagePushServiceUrl;

    @Autowired
    private SnatchMapper snatchMapper;

    @Autowired
    private TbSnatchRecordMapper snatchRecordMapper;

    @Autowired
    private TbSnatchGoodsMapper snatchGoodsMapper;

    @Autowired
    private SmsService smsService;

    @Autowired
    private UserAssetsService userAssetsService;


    @Autowired
    private TbOpActivityMapper tbOpActivityMapper;

    @Autowired
    private UserServiceFacade userServiceFacade;


    @Autowired
    private TbSnatchEggRecordMapper tbSnatchEggRecordMapper;

    @Autowired
    private TbSnatchWinningInfoMapper snatchWinningInfoMapper;


    @Override
    public APIResponse<RespSnatchRecords> snatchRecord(int uid, int issueID, int pageNo, int perPage) {
        issueID = issueID < 0 ? 0 : issueID;
        pageNo = pageNo < 0 ? 0 : pageNo;
        perPage = perPage <= 0 ? 10 : perPage;

        RespSnatchRecords snatchRecords = new RespSnatchRecords();
        List<RespSnatchRecordItem> recordItems = snatchMapper.getSnatchRecords(uid, issueID, pageNo, perPage);

        snatchRecords.setRecordItems(recordItems);


        return APIResponse.returnSuccess(snatchRecords);
    }


    @Override
    public APIResponse<List<RespSnatchWinningInfo>> myWinnings(int uid, int pageNo, int perPage) {
        pageNo = pageNo < 0 ? 0 : pageNo;
        perPage = perPage <= 0 ? 10 : perPage;
        return APIResponse.returnSuccess(snatchMapper.getWinningInfoList(pageNo, perPage, uid));
    }


    @Override
    public APIResponse<RespSnatchMine> mine(int uid, int type, int pageNo, int perPage) {
        pageNo = pageNo < 0 ? 0 : pageNo;
        perPage = perPage < 0 || perPage > 1000 ? 100 : perPage;

        //type= 0：全部 1：进行中 2：未中奖 3：获奖记录
        RespSnatchMine snatchMine = new RespSnatchMine();
        snatchMine.setMyInfo(getRespUserInfoBaseByUID(uid));


        List<RespSnatchMineItem> mines = getSnatchMines(uid, pageNo, perPage);
        if (type != 1 && type != 2 && type != 3) {
            snatchMine.setMines(mines);
            return APIResponse.returnSuccess(snatchMine);
        }

        List<RespSnatchMineItem> mineFilters = new ArrayList<>();
        for (RespSnatchMineItem mineItem : mines) {
            RespOpUserInfoItem winner = mineItem.getWinner();
            if (winner == null) {
                if (type == 1) {
                    mineFilters.add(mineItem);
                }
            } else {
                if (type == 2 && uid != winner.getUid()) {
                    mineFilters.add(mineItem);
                } else if (type == 3 && uid == winner.getUid()) {
                    mineFilters.add(mineItem);
                }
            }

        }
        snatchMine.setMines(mineFilters);
        return APIResponse.returnSuccess(snatchMine);

    }

    private RespOpUserInfoItem getRespUserInfoBaseByUID(int uid) {
        APIResponse<RespUserInfoBase> userInfoBase = userServiceFacade.queryUserInfo(uid);
        if (!userInfoBase.isRet()) {
            return null;
        }

        RespOpUserInfoItem ub = new RespOpUserInfoItem();
        RespUserInfoBase uib = userInfoBase.getData();
        ub.setUid(uid);
        ub.setUserName(uib.getNickName());
        ub.setImgUrl(uib.getIcon());

        return ub;
    }

    private List<RespSnatchMineItem> getSnatchMines(int uid, int pageNo, int perPage) {
        TbSnatchEggRecordExample example = new TbSnatchEggRecordExample();
        TbSnatchEggRecordExample.Criteria criteria = example.createCriteria();

        criteria.andUidEqualTo(uid);
        criteria.andIsDeleteEqualTo((byte) 0);
        example.setOrderByClause("create_time desc");
        example.setPage(new Page(pageNo * perPage, perPage));

        List<RespSnatchMineItem> mineItems = new ArrayList<>();

        List<TbSnatchEggRecord> eggRecords = tbSnatchEggRecordMapper.selectByExample(example);
        for (TbSnatchEggRecord record : eggRecords) {
            Integer issueID = record.getSnatchGoodsId();
            Integer eggRecordId = record.getId();

            TbSnatchGoods issueItem = getValidIssueInfoByID(issueID);
            if (issueItem == null) {
                continue;
            }

            RespPayCodes payCodes = queryCodes(uid, issueID, eggRecordId);

            RespSnatchMineItem mineItem = new RespSnatchMineItem();
            mineItem.setIssueID(issueID);
            mineItem.setRecordID(eggRecordId);
            mineItem.setCodes(payCodes);

            mineItem.setNeedTotal(issueItem.getAllCount());
            mineItem.setCurNum(issueItem.getJoinCount());

            RespBaseGoods goodsItem = getBaseGoods(issueID);
            mineItem.setGoodsItem(goodsItem);

            mineItem.setStartTime(issueItem.getStartTime());

            TbSnatchWinningInfo winningInfo = getWinningItemByIssueId(issueID);
            if (winningInfo != null) {
                mineItem.setEndTime(winningInfo.getLotteryTime());
                mineItem.setLuckyCode(doPadding(winningInfo.getLuckyCode(), PADDING_DIGIT));
                RespOpUserInfoItem userInfoItem = new RespOpUserInfoItem();
                userInfoItem.setUid(winningInfo.getUid());
                userInfoItem.setUserName(winningInfo.getNickName());
                userInfoItem.setImgUrl(winningInfo.getImgUrl());
                mineItem.setWinner(userInfoItem);
            }

            mineItems.add(mineItem);
        }

        return mineItems;
    }

    private TbSnatchWinningInfo getWinningItemByIssueId(int issueID) {
        TbSnatchWinningInfoExample example = new TbSnatchWinningInfoExample();
        TbSnatchWinningInfoExample.Criteria criteria = example.createCriteria();

        criteria.andSnatchGoodsIdEqualTo(issueID);

        List<TbSnatchWinningInfo> winningInfos = snatchWinningInfoMapper.selectByExample(example);
        if (winningInfos != null && !winningInfos.isEmpty()) {
            return winningInfos.get(0);
        }

        return null;
    }

    @Override
    public APIResponse getSnatchGoodsList(Byte type, Integer pageNum, Integer pageSize) {
        List<RespSnatchGoods> snatchGoodses = null;
        try {
            snatchGoodses = snatchMapper.getSnatchGoodsList(type, pageNum * pageSize, pageSize);
            long nowTime = System.currentTimeMillis();

            for (RespSnatchGoods snatchGoods : snatchGoodses) {
                Date startTime = snatchGoods.getStartTime();
                Date endTime = snatchGoods.getEndTime();
                if (startTime != null && endTime != null) {
                    if (startTime.getTime() > nowTime) {
                        snatchGoods.setGoodsState((byte) 1);  //活动未开始
                    } else if (nowTime >= startTime.getTime() && nowTime <= endTime.getTime()) {
                        snatchGoods.setGoodsState((byte) 2); //活动正在进行
                    } else if (nowTime > endTime.getTime()) {
                        snatchGoods.setGoodsState((byte) 3); //活动已结束
                    }
                }

                //设置用户商品夺宝获奖信息
                TbSnatchRecordExample snatchRecordExample = new TbSnatchRecordExample();
                TbSnatchRecordExample.Criteria snatchRecordCriteria = snatchRecordExample.createCriteria();
                snatchRecordCriteria.andIsDeleteEqualTo((byte) 0); //未删除记录
                snatchRecordCriteria.andSnatchGoodsIdEqualTo(snatchGoods.getIssueID()); //设置期号
                snatchRecordCriteria.andIsWinningEqualTo((byte) 1); //中奖
                List<TbSnatchRecord> tbSnatchRecords = snatchRecordMapper.selectByExample(snatchRecordExample);
                if (tbSnatchRecords != null && tbSnatchRecords.size() > 0) {
                    TbSnatchRecord snatchRecord = tbSnatchRecords.get(0);  //设置夺宝中奖信息
                    SnatchWinningInfo snatchWinningInfo = new SnatchWinningInfo();
                    snatchWinningInfo.setImgUrl(snatchRecord.getImgUrl());
                    snatchWinningInfo.setMobile(snatchRecord.getMobile());
                    snatchWinningInfo.setNickName(snatchRecord.getNickName());
                    snatchWinningInfo.setSnatchCode(doPadding(snatchRecord.getSnatchCode(), PADDING_DIGIT));
                    snatchWinningInfo.setIssueID(snatchRecord.getSnatchGoodsId());
                    snatchGoods.setWinningInfo(snatchWinningInfo);
                }
            }
        } catch (Exception e) {
            logger.error("获取商品列表失败", e.getMessage());
            return APIResponse.returnFail("获取商品列表失败");
        }

        return APIResponse.returnSuccess(snatchGoodses);
    }

    @Override
    public APIResponse generateSnatchNum(Integer issueID) {
        TbSnatchRecordExample snatchRecordExample3 = new TbSnatchRecordExample();
        TbSnatchRecordExample.Criteria snatchRecordcriteria3 = snatchRecordExample3.createCriteria();
        snatchRecordcriteria3.andSnatchGoodsIdEqualTo(issueID); //夺宝期号
        snatchRecordcriteria3.andIsDeleteEqualTo((byte) 0);  //未删除
        snatchRecordcriteria3.andIsWinningEqualTo((byte) 1); //已生成夺宝奖品码
        int count = snatchRecordMapper.countByExample(snatchRecordExample3);
        if (count > 0) {
            return APIResponse.returnFail("该期号夺宝奖品码已经生成！");
        }

        //同一期号最近50条记录的时间和
        long totalTime = getTotalTime(issueID);

        TbSnatchGoodsExample snatchGoodsExample = new TbSnatchGoodsExample();
        TbSnatchGoodsExample.Criteria snatchGoodsCriteria = snatchGoodsExample.createCriteria();
        snatchGoodsCriteria.andIsDeleteEqualTo((byte) 0);
        snatchGoodsCriteria.andIdEqualTo(issueID);
        List<TbSnatchGoods> tbSnatchGoodses = snatchGoodsMapper.selectByExample(snatchGoodsExample);

        if (tbSnatchGoodses != null && tbSnatchGoodses.size() > 0) {
            //产生夺宝中奖号
            long snatchCode = totalTime % (tbSnatchGoodses.get(0).getAllCount()) + 1;
            //更新用户夺宝中奖状态
            TbSnatchRecordExample snatchRecordExample2 = new TbSnatchRecordExample();
            TbSnatchRecordExample.Criteria snatchRecordcriteria2 = snatchRecordExample2.createCriteria();
            snatchRecordcriteria2.andSnatchGoodsIdEqualTo(issueID); //夺宝期号
            snatchRecordcriteria2.andSnatchCodeEqualTo((int) snatchCode);//
            TbSnatchRecord tbSnatchRecord = new TbSnatchRecord();
            tbSnatchRecord.setIsWinning((byte) 1); //设置中奖状态
            int result = snatchRecordMapper.updateByExampleSelective(tbSnatchRecord, snatchRecordExample2);

            if (result == 1) {
                //获取该期号下中奖人的信息
                TbSnatchRecordExample snatchRecordExample4 = new TbSnatchRecordExample();
                TbSnatchRecordExample.Criteria snatchRecordcriteria4 = snatchRecordExample4.createCriteria();
                snatchRecordcriteria4.andSnatchGoodsIdEqualTo(issueID); //夺宝期号
                snatchRecordcriteria4.andSnatchCodeEqualTo((int) snatchCode); //
                snatchRecordcriteria4.andIsWinningEqualTo((byte) 1);  //
                List<TbSnatchRecord> snatchRecords = snatchRecordMapper.selectByExample(snatchRecordExample4);

                if (snatchRecords != null && snatchRecords.size() > 0) {
                    TbSnatchRecord snatchRecord = snatchRecords.get(0);
                    final Integer uid = snatchRecord.getUid();
                    final String mobile = snatchRecord.getMobile();
                    RespBaseGoods baseGoods = getBaseGoods(issueID); //获取商品基本信息

                    TbSnatchWinningInfo tbSnatchWinningInfo = new TbSnatchWinningInfo();
                    tbSnatchWinningInfo.setSnatchGoodsId(issueID); //设置期号
                    tbSnatchWinningInfo.setNickName(snatchRecord.getNickName());
                    tbSnatchWinningInfo.setMobile(mobile);
                    tbSnatchWinningInfo.setUid(uid);
                    tbSnatchWinningInfo.setImgUrl(snatchRecord.getImgUrl());
                    if (baseGoods != null) {
                        tbSnatchWinningInfo.setGoodsImg(baseGoods.getGoodsImg());
                        tbSnatchWinningInfo.setGoodsName(baseGoods.getGoodsName());
                    }
                    tbSnatchWinningInfo.setLuckyCode((int) snatchCode);  //设置中奖码
                    int ret = snatchWinningInfoMapper.insertSelective(tbSnatchWinningInfo);   //插入中奖信息

                    if (ret == 1) {
                        //消息文案
                        final String msg = "亲爱的" + snatchRecord.getNickName() + "，恭喜您在鸟蛋夺宝中成功获得" + baseGoods.getGoodsName() + "，请保持手机通畅，客服人员将在3个工作日内联系您确认礼品信息。如有任何疑问，您可以致电客服热线：4008706616";
                        final String args[] = new String[2];
                        args[0] = "鸟蛋夺宝中获得" + baseGoods.getGoodsName();
                        args[1] = DOWNLOAD_LINK;
                        //推送消息和短信
                        pushMessage(uid, msg);
                        smsService.sendMessageToPhone(mobile, SmsApi.MESSAGE_TEMPLATE_OPERATIVE_SNATCH_NOTICE, args, true);
                    }

                }

            }
        }
        return APIResponse.returnSuccess();
    }


    /**
     * @param issueID 期号
     * @return
     */
    private RespBaseGoods getBaseGoods(Integer issueID) {
        List<RespBaseGoods> baseGoods = snatchMapper.getSnatchBaseGoodsList(issueID);
        if (baseGoods != null && baseGoods.size() > 0) {
            return baseGoods.get(0);
        }
        return new RespBaseGoods();
    }

    /**
     * 推送消息
     *
     * @paraid
     */
    public void pushMessage(Integer uid, String msg) {
        //消息推送
        ReqPushMessage reqPushMessage = new ReqPushMessage();
        PushMessage pushMessage = new PushMessage();
        pushMessage.setTag(BoxName.CLUB.value() + System.currentTimeMillis());
        Alert alert = new Alert();
        alert.setBody("系统通知：" + msg);
        pushMessage.setAlert(alert);

        pushMessage.setTo(Arrays.asList(uid));
        pushMessage.setLogo(LARK_LOGO);
        pushMessage.setLogoUrl("");
        Payload payload = new Payload();
        payload.setBoxType(BoxType.SYSTEM_MSG.value());
        payload.setMsgType(MsgType.NOTIFY_MSG.value());
        payload.setTitle(msg);
        payload.setTimestamp(System.currentTimeMillis());
        payload.setImg(SYSTEM_MESSAGE_URL);
        pushMessage.setPayload(payload);
        reqPushMessage.setMsgList(Arrays.asList(pushMessage));

        String postJson = JSON.toJSONString(reqPushMessage);
        logger.info("推送消息：" + postJson);
        String result = null;

        try {
            result = HttpClientUtils.postJson(messagePushServiceUrl, postJson);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("推送消息失败", e.getMessage());
        }
        logger.info("推送返回：" + result);
    }


    /**
     * 产生最近50条记录的create_time和
     *
     * @param issueID
     * @return
     */
    private long getTotalTime(Integer issueID) {
        TbSnatchRecordExample snatchRecordExample = new TbSnatchRecordExample();
        TbSnatchRecordExample.Criteria snatchRecordcriteria = snatchRecordExample.createCriteria();
        snatchRecordcriteria.andSnatchGoodsIdEqualTo(issueID); //夺宝期号
        snatchRecordcriteria.andIsDeleteEqualTo((byte) 0);  //未删除
        Page page = new Page();
        page.setBegin(0);
        page.setLength(50);
        snatchRecordExample.setPage(page);
        snatchRecordExample.setOrderByClause("create_time desc");  //时间降序
        List<TbSnatchRecord> tbSnatchRecords = snatchRecordMapper.selectByExample(snatchRecordExample);

        //产生最近50条记录的create_time和
        long totalTime = 0;
        if (tbSnatchRecords != null && tbSnatchRecords.size() > 0) {
            for (TbSnatchRecord tbSnatchRecord : tbSnatchRecords) {
                totalTime = totalTime + (long) tbSnatchRecord.getSnatchTime();
            }
        }
        return totalTime;
    }


    @Override
    public APIResponse<RespPayCodes> getCodes(int uid, int activityID, int issueID, int eggRecordID) {

        TbOpActivity activity = getValidActivityInfoByID(activityID);
        if (activity == null) {
            return APIResponse.returnFail("没有此活动ID对应的活动");
        }

        return APIResponse.returnSuccess(queryCodes(uid, issueID, eggRecordID));
    }

    private RespSnatchParticipantItem getParticipantItem(TbSnatchEggRecord record) {
        if (record == null) {
            return null;
        }
        RespSnatchParticipantItem item = new RespSnatchParticipantItem();
        item.setUid(record.getUid());
        item.setNickName(record.getNickName());
        item.setImgURL(record.getImgUrl());
        item.setDate(record.getCreateTime());
        item.setRecordID(record.getId());
        item.setTimes(record.getBuyNum());

        return item;
    }

    private RespPayCodes queryCodes(int uid, int issueID, int eggRecordID) {
        TbSnatchRecordExample example = new TbSnatchRecordExample();
        TbSnatchRecordExample.Criteria criteria = example.createCriteria();
        criteria.andUidEqualTo(uid);
        criteria.andSnatchGoodsIdEqualTo(issueID);
        criteria.andIsDeleteEqualTo((byte) 0);
        if (eggRecordID > 0) {
            criteria.andEggRecordIdEqualTo(eggRecordID);
        }

        example.setOrderByClause("create_time desc");


        List<TbSnatchRecord> snatchRecords = snatchRecordMapper.selectByExample(example);

        RespPayCodes payCodes = new RespPayCodes();
        List<RespPayCodeItem> items = new ArrayList<>();
        for (TbSnatchRecord record : snatchRecords) {
            items.add(new RespPayCodeItem(record.getCreateTime(), doPadding(record.getSnatchCode(), PADDING_DIGIT), record.getSnatchTime()));
        }
        payCodes.setCodes(items);
        return payCodes;
    }

    private String doPadding(int code, int width) {
        return StringUtils.leftPad(String.valueOf(code), width, '0');
    }

    @Override
    public APIResponse<RespSnatchParticipantsInfo> getParticipants(int activityID, int issueID, int pageNo, int perPage) {
        RespSnatchParticipantsInfo participantsInfo = new RespSnatchParticipantsInfo();

        participantsInfo.setPageNo(pageNo);
        participantsInfo.setPerPage(perPage);
        participantsInfo.setParticipants(getParticipantsList(issueID, pageNo, perPage));

        return APIResponse.returnSuccess(participantsInfo);
    }

    private List<RespSnatchParticipantItem> getParticipantsList(int issueID, int pageNo, int perPage) {
        pageNo = pageNo < 0 ? 0 : pageNo;
        perPage = perPage <= 0 ? 10 : perPage;

        TbSnatchEggRecordExample example = new TbSnatchEggRecordExample();
        TbSnatchEggRecordExample.Criteria criteria = example.createCriteria();
        criteria.andSnatchGoodsIdEqualTo(issueID);
        criteria.andIsDeleteEqualTo((byte) 0);

        example.setPage(new Page(pageNo * perPage, perPage));
        example.setOrderByClause("update_time desc");


        List<TbSnatchEggRecord> eggRecords = tbSnatchEggRecordMapper.selectByExample(example);

        List<RespSnatchParticipantItem> participantItems = new ArrayList<>();
        for (TbSnatchEggRecord eggRecord : eggRecords) {
            participantItems.add(getParticipantItem(eggRecord));
        }

        return participantItems;
    }

    @Override
    public APIResponse<Long> getEggsInfo(int uid) {
        return APIResponse.returnSuccess(getCurrentEggInfo(uid));
    }


    @Override
    public APIResponse<RespEnterSpec> enterSpec(int uid, int activityID, int issueID) {
        TbSnatchGoods snatchGoods = getValidIssueInfoByID(issueID);
        if (snatchGoods == null) {
            return APIResponse.returnFail("没有此期号对应的夺宝信息");
        }

        RespEnterSpec respEnterSpec = new RespEnterSpec();
        respEnterSpec.setIssueID(issueID);
        respEnterSpec.setGoods(getBaseGoods(issueID));
        respEnterSpec.setNeedNum(snatchGoods.getAllCount());
        respEnterSpec.setConsumedNum(snatchGoods.getJoinCount());

        if (uid <= 0) {
            respEnterSpec.setEggs(0);
            respEnterSpec.setCodes(new ArrayList<RespPayCodeItem>());
        } else {
            respEnterSpec.setEggs(getCurrentEggInfo(uid));
            respEnterSpec.setCodes(queryCodes(uid, issueID, 0).getCodes());
        }

        respEnterSpec.setPlayers(getParticipantsList(issueID, 0, 10));

        TbSnatchWinningInfo winningInfo = getWinningItemByIssueId(issueID);
        if (winningInfo != null) {
            respEnterSpec.setEndTime(winningInfo.getLotteryTime());
        }

        return APIResponse.returnSuccess(respEnterSpec);
    }


    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class)
    public synchronized APIResponse<RespPayCodes> doAnte(int uid, int activityID, final int issueID, int anteNum) {
        //1. 检查活动ID、期号ID的有效性
        //2. 是否有可用库存、购买量是否超库存
        //3. 鸟蛋是否足够
        //4. 生成购买码

        //首先要检查预生成订单是否存在
        //检查鸟蛋是否足够
        //检查是否有余票（含未超时的订单），如果没有余票了，可开奖

        TbOpActivity activity = getValidActivityInfoByID(activityID);
        if (activity == null) {
            return APIResponse.returnFail("没有此活动ID对应的活动");
        }

        TbSnatchGoods snatchGoods = getValidIssueInfoByID(issueID);
        if (snatchGoods == null) {
            return APIResponse.returnFail("没有此期号对应的夺宝信息");
        }

        if (snatchGoods.getAllCount() <= 0 || snatchGoods.getAllCount() > (int) Math.pow(10.0, 1.0 * PADDING_DIGIT)) {
            return APIResponse.returnFail("内部参数配置错误");
        }

        int curStock = snatchGoods.getAllCount() - snatchGoods.getJoinCount();
        if (curStock <= 0) {
            return APIResponse.returnFail("本期夺宝已参满");
        }

        APIResponse<RespUserInfoBase> userInfoBase = userServiceFacade.queryUserInfo(uid);
        if (!userInfoBase.isRet()) {
            return APIResponse.returnFail("用户信息有误");
        }


        if (anteNum > curStock) {
            return APIResponse.returnFail("本期可夺宝的数量不足");
        }

        long curEgg = getCurrentEggInfo(uid);
        int needEgg = anteNum * snatchGoods.getTimesEggs();
        if (needEgg <= 0) {
            return APIResponse.returnFail("内部配置错误");
        }
        if (curEgg < needEgg) {
            return APIResponse.returnFail("鸟蛋数不足");
        }

        //launch pay
        //generate codes
        //write to record
        //recalculate inventory
        //check if this round is over
        //return codes to user


        TbSnatchGoods snatchGoods_new = getValidIssueInfoByID(issueID);
        if (snatchGoods_new == null || !snatchGoods_new.getJoinCount().equals(snatchGoods.getJoinCount())) {
            return APIResponse.returnFail("剩余夺宝数据不一致，请刷新重试");
        }

        snatchMapper.updateTotalJoin(issueID, anteNum);
        List<RespPayCodeItemInternal> codes = genCodes(snatchGoods, anteNum);

        if (codes == null || codes.isEmpty()) {
            snatchMapper.updateTotalJoin(issueID, -1 * anteNum);
            return APIResponse.returnFail("剩余夺宝数不够");
        }


        RespUserAccumulateModify respModify = launchPay(uid, (long) needEgg);
        if (respModify == null) {
            return APIResponse.returnFail("内部鸟蛋支付失败");
        }


        Date now_t = new Date();
        batchInsertAndUpdate(snatchGoods, userInfoBase.getData(), codes, now_t);

        snatchGoods = getValidIssueInfoByID(issueID);
        if (snatchGoods == null) {
            return APIResponse.returnFail("内部错误");
        }
        if (snatchGoods.getAllCount().compareTo(snatchGoods.getJoinCount()) <= 0) {
            //notify user and push message to mq
            executor.schedule(new Runnable() {
                @Override
                public void run() {
                    generateSnatchNum(issueID);
                }
            }, LOTTERY_TIME_DELAY, TimeUnit.MINUTES);
        }


        RespPayCodes payCodes = new RespPayCodes();
        List<RespPayCodeItem> codeItems = new ArrayList<>();
        for (RespPayCodeItemInternal itemInternal : codes) {
            codeItems.add(getRespPayCodeItem(itemInternal));
        }
        payCodes.setCodes(codeItems);

        return APIResponse.returnSuccess(payCodes);

    }

    private RespPayCodeItem getRespPayCodeItem(RespPayCodeItemInternal itemInternal) {
        if (itemInternal == null) {
            return null;
        }

        return new RespPayCodeItem(itemInternal.getGenDate(), doPadding(itemInternal.getCode(), PADDING_DIGIT), itemInternal.getTime());
    }


    private synchronized void batchInsertAndUpdate(TbSnatchGoods goods, RespUserInfoBase userBase, List<RespPayCodeItemInternal> codes, Date now_t) {
        if (goods == null || userBase == null || codes == null || codes.isEmpty()) {
            return;
        }

        TbSnatchEggRecord recordEgg = new TbSnatchEggRecord();
        recordEgg.setUid(userBase.getUid());
        recordEgg.setNickName(userBase.getNickName());
        recordEgg.setImgUrl(userBase.getIcon());
        recordEgg.setSnatchGoodsId(goods.getId());
        recordEgg.setBuyNum(codes.size());
        recordEgg.setBuyEggNum(codes.size() * goods.getTimesEggs());
        recordEgg.setIsDelete((byte) 0);
        recordEgg.setCreateTime(now_t);
        recordEgg.setUpdateTime(now_t);
        tbSnatchEggRecordMapper.insertSelective(recordEgg);


        TbSnatchRecord record = new TbSnatchRecord();

        record.setEggRecordId(recordEgg.getId());
        record.setSnatchGoodsId(goods.getId());
        record.setUid(userBase.getUid());
        record.setNickName(userBase.getNickName());
        record.setMobile(userBase.getMobile());
        record.setImgUrl(userBase.getIcon());

        record.setIsDelete((byte) 0);
        record.setIsWinning((byte) 0);
        record.setCreateTime(now_t);
        record.setUpdateTime(now_t);

        for (RespPayCodeItemInternal code : codes) {
            record.setSnatchCode(code.getCode());
            record.setSnatchTime(getMiliTime(code.getGenDate()));

            snatchRecordMapper.insertSelective(record);
        }

//        TbSnatchGoods issueItem = snatchGoodsMapper.selectByPrimaryKey(goods.getId());
//        goods.setJoinCount(issueItem.getJoinCount() + codes.size());
//        goods.setUpdateTime(now_t);
//        snatchGoodsMapper.updateByPrimaryKeySelective(goods);

    }

    private int getMiliTime(Date now_t) {
        SimpleDateFormat sdFormat = new SimpleDateFormat("HHmmssSSS");
        return Integer.parseInt(sdFormat.format(now_t));
    }

    private List<RespPayCodeItemInternal> genCodes(TbSnatchGoods goods, int wantCnt) {
        if (goods == null || wantCnt <= 0 || wantCnt > (goods.getAllCount() - goods.getJoinCount())) {
            return null;
        }

        Set<Integer> leftSet = new HashSet<>();
        for (int i = 0; i < goods.getAllCount(); ++i) {
            leftSet.add(i + 1);
        }

        Set<Integer> existsSet = new HashSet<>(getExistingCodes(goods.getId()));
        leftSet.removeAll(existsSet);


        if (wantCnt > leftSet.size()) {
            return null;
        }

        List<Integer> leftList = new ArrayList<>(leftSet);

        List<RespPayCodeItemInternal> elts = new ArrayList<>();
        for (int i = 0; i < wantCnt; ++i) {
            Collections.shuffle(leftList);
            int randomIndex = (int) (Math.random() * leftList.size());
            int randVal = leftList.get(randomIndex);
            Date now_t = new Date();
            elts.add(new RespPayCodeItemInternal(now_t, randVal, getMiliTime(now_t)));
            leftList.remove(randomIndex);
        }

        return elts;

    }


    private TbOpActivity getValidActivityInfoByID(int activityID) {
        TbOpActivity item = tbOpActivityMapper.selectByPrimaryKey(activityID);
        Date now_d = new Date();
        if (item == null || item.getIsClose().equals((byte) 1) || item.getStarttime().after(now_d) || item.getEndtime().before(now_d)) {
            return null;
        }
        return item;
    }

    private TbSnatchGoods getValidIssueInfoByID(int issueID) {
        TbSnatchGoods item = snatchGoodsMapper.selectByPrimaryKey(issueID);
        if (item == null || item.getIsDelete().equals((byte) 1)) {
            return null;
        }
        return item;
    }


    private List<Integer> getExistingCodes(int issueID) {
        List<TbSnatchRecord> tbSnatchRecords;
        TbSnatchRecordExample example = new TbSnatchRecordExample();
        TbSnatchRecordExample.Criteria criteria = example.createCriteria();
        criteria.andSnatchGoodsIdEqualTo(issueID);

        tbSnatchRecords = snatchRecordMapper.selectByExample(example);
        List<Integer> lst = new ArrayList<>();
        for (TbSnatchRecord record : tbSnatchRecords) {
            lst.add(record.getSnatchCode());
        }
        return lst;
    }

    private Long getCurrentEggInfo(int uid) {
        APIRequest<ReqUserInfo> apiRequest = new APIRequest<>();
        ReqUserInfo reqUserInfo = new ReqUserInfo();
        reqUserInfo.setUid(uid);
        reqUserInfo.setAssetsType(AssetsType.ASSETS_EGGS.getId().byteValue());
        reqUserInfo.setSystemCode(Constants.OPERATIVE_SYSTEM_CODE);
        reqUserInfo.setBusinessCode(Constants.OPERATIVE_DUOBAO_CODE);
        apiRequest.setData(reqUserInfo);

        APIResponse<List<TbUserAssets>> respAssets = null;
        try {
            respAssets = userAssetsService.getUserAccumulateList(apiRequest);
        } catch (Exception e) {
            logger.warn("uid:{}, error:{} while trying to get eggs", uid, e.getMessage());
            throw new BizException("error_while_trying_to_get_eggs");
        }

        if (respAssets == null || !respAssets.isRet() || respAssets.getData() == null) {
            return 0L;
        }

        List<TbUserAssets> respUserAssets = respAssets.getData();
        for (TbUserAssets tua : respUserAssets) {
            if (tua.getAssetsType().equals(AssetsType.ASSETS_EGGS.getId().byteValue())) {
                return tua.getAccumulate();
            }
        }

        return 0L;

    }

    private RespUserAccumulateModify launchPay(int uid, long consumeEggs) {
        if (uid <= 0 || consumeEggs <= 0) {
            return null;
        }

        ReqUserAccumulateModify modify = new ReqUserAccumulateModify();
        modify.setUid(uid);
        modify.setAssetsType(AssetsType.ASSETS_EGGS.id.byteValue());
        modify.setSystemCode(Constants.OPERATIVE_SYSTEM_CODE);
        modify.setReceiver("百灵鸟");
        modify.setPayReceive("运营活动消耗");
        modify.setBusinessCode(Constants.OPERATIVE_DUOBAO_CODE);
        modify.setInfo("鸟蛋夺宝");
        modify.setAccumulate(consumeEggs);

        APIRequest<ReqUserAccumulateModify> apiRequest = new APIRequest<>();
        apiRequest.setData(modify);

        APIResponse<RespUserAccumulateModify> response = userAssetsService.modifyAccumulate(apiRequest);
        if (response == null || !response.isRet()) {
            return null;
        }

        return response.getData();
    }


    @Override
    public APIResponse<RespWinningInfos> getWinningInfoList(Integer pageNum, Integer pageSize) {
        RespWinningInfos respWinningInfos = new RespWinningInfos();
        List<RespSnatchWinningInfo> snatchWinningInfos = null;
        try {
            Date currentTime = new Date();
            snatchWinningInfos = snatchMapper.getWinningInfoList(pageNum * pageSize, pageSize, 0);
            if (snatchWinningInfos != null && snatchWinningInfos.size() > 0) {
                for (RespSnatchWinningInfo snatchWinningInfo : snatchWinningInfos) {
                    snatchWinningInfo.setLuckyCode(doPadding(Integer.parseInt(snatchWinningInfo.getLuckyCode()), PADDING_DIGIT));
                }
            }

            respWinningInfos.setCurrentTime(currentTime);
            respWinningInfos.setSnatchWinningInfos(snatchWinningInfos);
        } catch (Exception e) {
            logger.error("获取中奖名单失败:{}", e.getMessage());
            return APIResponse.returnFail("获取中奖名单失败");
        }
        return APIResponse.returnSuccess(respWinningInfos);
    }
}
